// EMIR - Ekvitermni mikroprocesorova regulace
// Copyright (C) 2002-2012  Ales Povalac
//
// $Id: main.c 172 2012-10-08 15:40:37Z alpov@alpov.net $
//
// Based on VirtualSerial demo from LUFA Library
// Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <string.h>
#include <stdio.h>
#include <LUFA/Drivers/USB/USB.h>
#include "timer.h"
#include "Descriptors.h"
#include "hw.h"

#include "dev_common.inc"
#include "dev_ioboard.inc"
#include "dev_terminal.inc"

#define CDC_Connected() \
    ((USB_DeviceState == DEVICE_STATE_Configured) && \
    (VirtualSerial_CDC_Interface.State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR))


// --------- LUFA internals begin ---------

/** LUFA CDC Class driver interface configuration and state information. This structure is
 *  passed to all CDC Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
    {
        .Config =
            {
                .ControlInterfaceNumber   = 0,
                .DataINEndpoint           =
                    {
                        .Address          = CDC_TX_EPADDR,
                        .Size             = CDC_TXRX_EPSIZE,
                        .Banks            = 1,
                    },
                .DataOUTEndpoint =
                    {
                        .Address          = CDC_RX_EPADDR,
                        .Size             = CDC_TXRX_EPSIZE,
                        .Banks            = 1,
                    },
                .NotificationEndpoint =
                    {
                        .Address          = CDC_NOTIFICATION_EPADDR,
                        .Size             = CDC_NOTIFICATION_EPSIZE,
                        .Banks            = 1,
                    },
            },
    };

/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
    CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
    CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
}

// ---------- LUFA internals end ----------


ISR(TIMER0_COMPA_vect, ISR_BLOCK)
{
    TickCounter++;
}


void emir2_receive_command(void)
{
    static char buffer[BUFFER_SIZE];
    static uint16_t buffer_pos = 0;
    uint16_t c;

    while (CDC_Connected() && ((c = getchar()) != EOF)) {
        if (c == '\r') c = '\n'; // windows hyperterminal workaround

        buffer[buffer_pos++] = (uint8_t)(c);
        if (buffer_pos > BUFFER_SIZE)
            buffer_pos = 0;

        if ((c == '\n') && (buffer_pos >= 3)) {
            buffer[buffer_pos-1] = '\0';
            buffer_pos = 0;

            switch (buffer[0]) {
                case 's':
                    // system calls
                    emir2_system_command(buffer);
                    break;
                    
                case 'w':
                    // 1-wire subsystem
                    emir2_onewire_command(buffer);
                    break;
#if EMIR2DEV == 1
                case 'i':
                    // I/O subsystem
                    emir2_io_command(buffer);
                    break;
#elif EMIR2DEV == 2
                case 'd':
                    // LCD display subsystem
                    emir2_lcd_command(buffer);
                    break;

                case 'b':
                    // buttons
                    emir2_button_command(buffer);
                    break;
#endif
                default:
                    // default error handler
                    printf_P(PSTR("unknown command\n"));
                    break;
            }
        }
    }
}


void emir2_control_task(void)
{
#if EMIR2DEV == 1
    // update LEDs according to inputs
    emir2_update_leds();
#elif EMIR2DEV == 2
    // check buttons and generate async messages
    emir2_terminal_buttons();
#endif
}


int main(void)
{
    // SysTick init
    TCCR0A = _BV(WGM01);
    TCCR0B = _BV(CS01) | _BV(CS00);
    OCR0A = 124;
    TIMSK0 |= _BV(OCIE0A);

    // hardware init
    USB_Init();
#if EMIR2DEV == 1
    emir2_ioboard_init();
#elif EMIR2DEV == 2
    emir2_terminal_init();
#endif

    // character stream for USB interface */
    static FILE USBSerialStream;
    CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, &USBSerialStream);
    stdin  = &USBSerialStream;
    stdout = &USBSerialStream;

    wdt_enable(WDTO_4S);
    sei();

    while (1)
    {
        // receive CDC commands and execute them
        emir2_receive_command();
        
        // check pending input events
        emir2_control_task();

        // LUFA kernel tasks
        CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
        USB_USBTask();
        
        // watchdog reset
        wdt_reset();
    }
}

